package me.yh.plugin.demo.services.impl;

import me.yh.plugin.PluginClassLoader;
import me.yh.plugin.PluginManager;
import me.yh.plugin.demo.services.PluginServiceManager;
import me.yh.plugin.demo.services.ServiceInstance;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by zhouyanhui on 16-5-28.
 */
@Service("pluginServiceManager")
public class PluginServiceManagerImpl implements PluginServiceManager {
	private SimpleServiceInstance simpleServiceInstance= null;
	private static final Logger logger = LoggerFactory.getLogger(PluginServiceManagerImpl.class);
	private PluginClassLoader classLoader = null;
	private PluginManager pluginManager = new PluginManager();
	private ConcurrentHashMap<String, ServiceInstance> serviceMap = new ConcurrentHashMap<>();

	private ConcurrentHashMap<String, Object> serviceManipulatorLock = new ConcurrentHashMap();
	private Object lockObj = new Object();

	private boolean tryLock(String pluginName) {
		return null == this.serviceManipulatorLock.putIfAbsent(pluginName, this.lockObj);
	}

	private void unlock(String pluginName) {
		this.serviceManipulatorLock.remove(pluginName);
	}

	@Override
	public void onNew(String jarUri, String pluginName, String service) {
		while (!tryLock(service)) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		try {
			rebuildService(jarUri, pluginName, service);
		} finally {
			unlock(service);
		}

	}

	/**
	 * 重新加载服务
	 * 
	 * @param jarUri
	 * @param pluginName
	 * @param service
	 */
	private void rebuildService(String jarUri, String pluginName, String service) {

		try {
			pluginManager.loadPlugin(jarUri, pluginName);
			classLoader = pluginManager.getLoader(pluginName);
		} catch (Exception e) {
			// TODO logger
			logger.info("load plugin failed" + e);
			return;
		}
		try {
			Class<?> aClass = classLoader.loadClass(service);
			simpleServiceInstance = new SimpleServiceInstance(aClass, aClass.newInstance());
			serviceMap.put(service, simpleServiceInstance);
		} catch (ClassNotFoundException e) {
			// TODO error
			logger.info("rebuildService: " + e);
		} catch (InstantiationException e) {
			e.printStackTrace();
			logger.info("rebuildService: " + e);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			logger.info("rebuildService: " + e);
		}
	}

	private void removeService(String pluginName, String service) {
		serviceMap.remove(service);
		pluginManager.unloadPlugin(pluginName);
	}

	@Override
	public void onChange(String jarUri, String pluginName, String service) {
		while (!tryLock(service)) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		try {
			removeService(pluginName, service);
			rebuildService(jarUri, pluginName, service);
		} finally {
			unlock(service);
		}
	}

	@Override
	public void onRemove(String pluginName, String service) {
		while (!tryLock(service)) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		try {
			removeService(pluginName, service);
		} finally {
			unlock(service);
		}
	}

	@Override
	public ServiceInstance getService(String serviceName) {
		return serviceMap.get(serviceName);
	}

}
